#include "config.h"

#include <regex.h>
#include <sqlite3.h>

#define DBG_SUBSYS S_LIBINTERFACE

#include "lich_api.h"

#include "../../storage/chunk/chunk_ops.h"
#include "../../storage/storage/locator_rpc.h"
#include "license.h"
#include "utils.h"
#include "dbg.h"
#include "diskmd_recovery.h"


static int __lookup_find = 0;
static fileid_t unlink_fid;    //skip /system/unlink directory

typedef enum {
        OP_NULL,
        OP_LIST,
        OP_MKDIR,
        OP_TOUCH,
        OP_UNLINK,

        OP_RMDIR,
        OP_STAT,
        OP_LOOKUP,
        OP_CHUNKSET,
        OP_CMP,

        OP_MOVE,
        OP_CHUNK_INFO,
        OP_CONNECTION,
        OP_SCAN,
        OP_RECOVER,

        OP_RECOVERCHUNK,
        OP_PAXOSDUMP,
        OP_BLANCEOBJ,
        OP_ALLOCATE,
        OP_TRUNCATE,

        OP_MD5SUM,
        OP_TIER,
        OP_PRIORITY,
        OP_CHUNK_MOVE,
        OP_CHUNK_STAT,

        OP_DB_DUMP,
        OP_DB_MODE,
        OP_BASE64,
        OP_CRC32,
        OP_MULTPATH,

        OP_LOCALIZE,
        OP_WRITEBACK,
        OP_CHUNK_DUMP,
        OP_CHUNK_FILL,
        OP_CHUNK_CLEANUP,

        OP_BOARDCAST,
        OP_RMVOL_FORCE,
        OP_DISPATCH,
        OP_READRAW,
        OP_VFMSET,

        OP_VFMGET,
        OP_DISCONNECT,

        // -- END
} admin_op_t;

static void usage()
{
        fprintf(stderr, "\nusage:\n"
                "lich.inspect --list  <chkid/path>\n"
                "lich.inspect --mkdir <path> [-P 0|1] [-e k+r]\n"
                "lich.inspect --touch <path> [-P 0|1]\n"
                "lich.inspect --rmdir <path>\n"
                "lich.inspect --unlink <path>\n"
                "lich.inspect --stat <chkid[/pool]/path>\n"
                "lich.inspect --lookup <chkid[/pool]/path>\n"
                "lich.inspect --allocate <chkid[/pool]/path> [-p|--pithy] [-f|--fill]\n"
                "lich.inspect --truncate <chkid[/pool]/path> <size>\n"
                "\n"
                "lich.inspect --move, -m  <chkid[/pool]/path> <nodename> [--async]\n"
                "lich.inspect --recover <path> [--deep] [--verbose|-v]\n"
                "lich.inspect --scan <path> [--deep] [--verbose|-v]\n"
                "lich.inspect --rmvolforce <path>\n"
                "lich.inspect --dispatch <pool> <site> <repnum>\n"
                "\n"
                "lich.inspect --tier <path> [0|1]\n"
                "lich.inspect --priority <path> [default | 0|1]\n"
                "lich.inspect --localize <path> [0|1]\n"
                "lich.inspect --writeback <path> [0|1]\n"
                "lich.inspect --multipath <path> [0|1]\n"
                "\n"
                "lich.inspect --vfmset <chkid/path> <clock:node1,node2>\n"
                "lich.inspect --vfmget <chkid/path>\n"
                "\n"
                "lich.inspect --chunkset  <chkid/path> <nodename> <clean | dirty | check>\n"
                "lich.inspect --chunkmove <chkid[/pool]> <nodename,nodename,...>\n"
                "lich.inspect --chunkstat <chkid[/pool]>\n"
                "lich.inspect --chunkinfo <chkid[/pool]/path> [-p|--pithy] [-v]\n"
                "lich.inspect --chunkdump <chkid/path> [dst]\n"
                "lich.inspect --chunkfill <chkid[/pool]/path> [src] [-f]\n"
                "lich.inspect --chunkcleanup <path>   /*very dangerous, be careful!*/\n"
                "lich.inspect --boardcast <chkid>\n"
                "\n"
                "lich.inspect --connection <path>\n"
                "lich.inspect --disconnect <path> <hostname> <addr>\n"
                "\n"
                // "lich.inspect --md5sum <path> [--pool <pool>]\n"
                "lich.inspect --dbdump <idx/all> [metadata|raw]\n"
                "lich.inspect --dbmode <idx/all>\n"
                "lich.inspect --base64 <encode/decode> <chkid/base64code> [-p|--pithy]\n"
                "lich.inspect --crc32 <path>\n"
                "\nexplain:\n"
                "        get the value of tier/priority/localize/writeback/multipath if omit [arg],\n"
                "        else it was set the value\n"
        );
}

#if 0
static int __lich_config_dump()
{
        int ret;
        int parallel = 0, expdata = 0;
        int parallel_recover = 0, parallel_balance = 0;
        char hostname[MAX_NAME_LEN];

        ret = conf_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        dbg_sub_init();

        ret = net_gethostname(hostname, MAX_NAME_LEN);
        if (unlikely(ret)) {
                if (ret == ECONNREFUSED)
                        strcpy(hostname, "N/A");
                else
                        GOTO(err_ret, ret);
        }

        ret = balance_parallel_get(&parallel, &expdata, &parallel_balance);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = recover_parallel_get(&parallel, &expdata, &parallel_recover);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        printf("globals.clustername:%s\n"
                        "globals.hostname:%s\n"
                        "globals.home:%s\n"
                        "globals.localize:%d\n"
                        "globals.wmem_max:%llu\n"
                        "globals.rmem_max:%llu\n"
                        "globals.replica_max:%u\n"
                        "globals.crontab:%u\n"
                        "globals.nohosts:%s\n"
                        "globals.cgroup:%s\n"
                        "globals.testing:%s\n"
                        "metadata.meta:%d\n"
                        "iscsi.iqn:%s\n"
                        "iscsi.port:%d\n"
                        "parallel_recover:%d\n"
                        "parallel_balance:%d\n",
                        gloconf.cluster_name,
                        hostname,
                        gloconf.home,
                        gloconf.localize,
                        (LLU)gloconf.wmem_max,
                        (LLU)gloconf.rmem_max,
                        LICH_REPLICA_MAX,
                        gloconf.crontab,
                        gloconf.nohosts ? "on" : "off",
                        gloconf.cgroup ? "on" : "off",
                        gloconf.testing ? "on" : "off",
                        mdsconf.meta,
                        sanconf.iqn,
                        sanconf.iscsi_port,
                        parallel_recover,
                        parallel_balance);

        return 0;
err_ret:
        return ret;
}
#endif

static int __inspect_init(int op)
{
        int ret, retry = 0;

        ret = env_init_simple("lich.inspect");
        if (unlikely(ret))
                GOTO(err_ret, ret);

retry:
        ret = network_connect_master();
        if (unlikely(ret)) {
                ret = _errno(ret);
                if (ret == EAGAIN || ret == ENOSPC) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, gloconf.rpc_timeout * 4, (1000 * 1000));
                } else if (ret == EHOSTDOWN) {
                        DWARN("master not online\n");
                        USLEEP_RETRY(err_ret, ret, retry, retry, gloconf.master_timeout + 2, (1000 * 1000));
                        GOTO(err_ret, ret);
                }
                        GOTO(err_ret, ret);
        }

        ret = stor_init(NULL, -1);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (op == OP_MOVE || op == OP_RECOVER || op == OP_SCAN || op == OP_ALLOCATE) {
                ret = storage_license_check();
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_list_pool(const fileid_t *fileid)
{
        int ret, delen, done = 0;
        uint64_t offset = 0, offset2 = 0;
        struct dirent *de;
        void *de0;
        char _chkinfo[CHKINFO_MAX], tmp[MAX_BUF_LEN];
        chkinfo_t *chkinfo;
        char uuid[MAX_NAME_LEN] = {};

        get_uuid(uuid);

        ret = stor_listpool_open(fileid, uuid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        de0 = NULL;
        chkinfo = (void *)_chkinfo;
        while (done == 0) {
                ret = stor_listpool(fileid, uuid, offset, &de0, &delen);
                if (unlikely(ret))
                        GOTO(err_close, ret);

                if (delen == 0)
                        break;

                offset2 = 0;
                dir_for_each(de0, delen, de, offset2) {
                        if (strlen(de->d_name) == 0) {
                                done = 1;
                                goto out;
                        } else if (delen - offset2 < sizeof(*de) + MAX_NAME_LEN) {
                                break;
                        }

                        offset += de->d_reclen;

                        ret = md_lookup_byname1(fileid, de->d_name, chkinfo, NULL);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);

                        CHKINFO_STR(chkinfo, tmp);

                        printf("%s --> %s\n", de->d_name, tmp);
                }

                yfree((void **)&de0);
        }

out:
        if (de0)
                yfree((void **)&de0);

        stor_listpool_close(fileid, uuid);
        return 0;
err_close:
        stor_listpool_close(fileid, uuid);
err_ret:
        return ret;
}

static int __lich_list_snapshot(const fileid_t *fileid)
{
        int ret, done = 0;
        uint64_t offset = 0, offset2 = 0;
        struct dirent *de;
        char de0[BIG_BUF_LEN];
        int delen = BIG_BUF_LEN;
        char _chkinfo[CHKINFO_MAX], tmp[MAX_BUF_LEN];

        char snap_name[MAX_NAME_LEN];
        long snap_ver, snap_from;
        chkinfo_t *chkinfo;
        char uuid[MAX_NAME_LEN] = {};

        get_uuid(uuid);

        ret = stor_snapshot_listopen(fileid, uuid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        chkinfo = (void *)_chkinfo;
        while (done == 0) {
                ret = stor_snapshot_list(fileid, uuid, offset, de0, &delen);
                if (unlikely(ret))
                        GOTO(err_close, ret);

                if (delen == 0)
                        break;

                offset2 = 0;
                dir_for_each(de0, delen, de, offset2) {
                        if (strlen(de->d_name) == 0) {
                                done = 1;
                                goto out;
                        } else if (delen - offset2 < sizeof(*de) + MAX_NAME_LEN) {
                                break;
                        }

                        offset += de->d_reclen;

                        ret = sscanf(de->d_name, "%s %ld %ld", snap_name, &snap_ver, &snap_from);
                        if (ret != 3)
                                continue;

                        ret = md_lookup_byname1(fileid, snap_name, chkinfo, NULL);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        
                        CHKINFO_STR(chkinfo, tmp);

                        printf("%s --> %s\n", de->d_name, tmp);
                }

                yfree((void **)&de0);
        }

out:
        stor_snapshot_listclose(fileid, uuid);
        return 0;
err_close:
        stor_snapshot_listclose(fileid, uuid);
err_ret:
        return ret;
}

static int __lich_list(const char *pool, const char *path)
{
        int ret;
        fileid_t fileid;

        YASSERT(path);
        YASSERT(strlen(path));

        ret = md_chunk_getid(pool, path, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (fileid.type == __VOLUME_CHUNK__) {
                ret = __lich_list_snapshot(&fileid);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                ret = __lich_list_pool(&fileid);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_dump(const char *pool, const char *path, int verbose, int pithy)
{
        int ret;
        chkid_t id;
        char buf[MAX_BUF_LEN];

        verbose = 1;

        if (isdigit(path[0])) {
                int diskid = atoi(path);
                diskmd_get_disk_chunkinfo(diskid);
        } else {
                if (strcmp(path, "/") == 0) {
                        ret = md_root(&id, pool);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        ret = md_chunk_getid(pool, path, &id);
                        if (unlikely(ret)) {
                                GOTO(err_print, ret);
                        }
                }

                ret = stor_stat(pool, &id, buf, verbose);
                if (unlikely(ret)) {
                        GOTO(err_print, ret);
                }

                printf("%s\n", buf);

                if (verbose && pithy && id.type == __VOLUME_CHUNK__) {
                        ret = stor_stat_file(pool, &id);
                        if (unlikely(ret)) {
                                GOTO(err_ret, ret);
                        }
                }
        }

        return 0;
err_print:
        if (ret == EAGAIN)
                printf("\x1b[1;31m  *%s : offline\x1b[0m\n", path);
err_ret:
        return ret;
}

static int __lich_cmp_dump(const char *pool, const chkid_t *chkid, char *_buf)
{
        int ret, i, err;
        sha1_result_t *result;
        char buf[MAX_BUF_LEN], tmp[MAX_BUF_LEN];
        chkinfo_t *chkinfo;

        chkinfo = (void *)tmp;
        ret = md_chunk_getinfo(pool, NULL, chkid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        result = (void *)buf;
        ret = chunk_proto_ops_get(NULL)->sha1(chkinfo, result);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for (err = 0, i = 1; i < (int)result->count; i++) {
                if (strcmp(result->md[0], result->md[i]) != 0) {
                        snprintf(_buf, MAX_BUF_LEN, "\x1b[1;31m"CHKID_FORMAT" %s --> %s error\x1b[0m",
                                        CHKID_ARG(chkid), result->md[0], result->md[i]);
                        err++;
                }
        }

        if (err == 0) {
                snprintf(_buf, MAX_BUF_LEN, ""CHKID_FORMAT" sha1 %s",
                                CHKID_ARG(chkid), result->md[0]);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_cmp(const char *pool, const char *path)
{
        int ret;
        fileid_t fileid;
        char tmp[MAX_BUF_LEN];
        chkid_t chkid;
        uint64_t i, chknum;
        fileinfo_t fileinfo;
        int retry;

        ret = md_chunk_getid(pool, path, &fileid);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        ret = __lich_cmp_dump(pool, &fileid, tmp);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        printf("check %s:\n", tmp);

        if (fileid.type == __VOLUME_CHUNK__) {
                retry = 0;
retry:
                ret = md_getattr(&fileid, &fileinfo);
                if (unlikely(ret)) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                }

                chknum = size2chknum(fileinfo.size, &fileinfo.ec);

                printf("chunknum: %lu\n", chknum);
                for (i = 0; i < chknum; i++) {
                        fid2cid(&chkid, &fileid, i);

                        retry = 0;
retry2:
                        ret = __lich_cmp_dump(pool, &chkid, tmp);
                        if (unlikely(ret)) {
                                if (ret == ENOENT) {
                                        continue;
                                } else {
                                        USLEEP_RETRY(err_ret, ret, retry2, retry, 50, (100 * 1000));
                                }
                        }

                        printf("check "CHKID_FORMAT"[%llu]: %s\n", CHKID_ARG(&fileid), (LLU)i, tmp);
                }
        }

        return 0;
err_ret:
        return ret;
}

static int __str2stat(const char *stat, int *_s)
{
        int ret, s;
        if (strcmp(stat, "clean") == 0) {
                s = __S_CLEAN;
        } else if (strcmp(stat, "check") == 0) {
                s = __S_CHECK;
        } else if (strcmp(stat, "dirty") == 0) {
                s = __S_DIRTY;
        } else {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        *_s = s;

        return 0;
err_ret:
        return ret;
}

static int __lich_chunkset(const char *pool, const char *path, const char *node, const char *_status)
{
        int ret, status;
        chkid_t chkid;
        nid_t nid;
        char buf[MAX_BUF_LEN];

        printf("set chunk %s @ %s status %s\n", path, node, _status);

retry:
        printf("That dangerous, are you sure what are you doing? (Yes/No)");

        ret = scanf("%s", buf);
        if (ret != 1) {
                ret = EINVAL;
                goto retry;
        }

        if (strcasecmp(buf, "No") == 0) {
                ret = EPERM;
                GOTO(err_ret, ret);
        } else if (strcasecmp(buf, "Yes") == 0) {
                //pass
        } else {
                goto retry;
        }

        ret = md_chunk_getid(pool, path, &chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = network_connect_byname(node, &nid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = __str2stat(_status, &status);
        if (unlikely(ret))
                GOTO(err_ret, ret);

#if 0
        ret = md_parent_get(&chkid, &parent);
        if (unlikely(ret))
                GOTO(err_ret, ret);
#endif

        ret = md_chunk_set(NULL, &chkid, &nid, status);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __lich_vfm_set__(const chkid_t *chkid, const vfm_t *vfm)
{
        int ret, i;
        chkid_t volid, tid, _chkid;
        fileinfo_t fileinfo;
        uint32_t max, chknum;
        char tmp[MAX_BUF_LEN];

        if (chkid->type == __VOLUME_CHUNK__) {
                volid = *chkid;
                ret = md_getattr(&volid, &fileinfo);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                chknum = size2chknum(fileinfo.size, NULL);
                max = chknum2tabnum(chknum);

                for (i = 0; i < max; i++) {
                        tid = volid;
                        tid.idx = i;
                        tid.type = __VOLUME_SUB_CHUNK__;

                        vfm_dump(vfm, tmp);

                        _chkid = tid;
                        _chkid.idx = tid.idx * FILE_PROTO_EXTERN_ITEM_COUNT;
                        _chkid.type = __RAW_CHUNK__;
                        
                        ret = md_vfm_set(&volid, &_chkid, vfm);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);

                        printf("set "CHKID_FORMAT" vfm %s\n", CHKID_ARG(&tid), tmp);
                }
        } else if (chkid->type == __RAW_CHUNK__) {
                cid2fid(&volid, chkid);

                vfm_dump(vfm, tmp);
                ret = md_vfm_set(&volid, chkid, vfm);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                printf("set "CHKID_FORMAT" vfm %s\n", CHKID_ARG(chkid), tmp);
        } else if (chkid->type == __VOLUME_SUB_CHUNK__) {
                tid = *chkid;
                cid2fid(&volid, chkid);

                _chkid = tid;
                _chkid.idx = tid.idx * FILE_PROTO_EXTERN_ITEM_COUNT;
                _chkid.type = __RAW_CHUNK__;
                        
                vfm_dump(vfm, tmp);
                ret = md_vfm_set(&volid, &_chkid, vfm);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                printf("set "CHKID_FORMAT" vfm %s\n", CHKID_ARG(chkid), tmp);
        } else {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}


static int __lich_vfm_set(const char *pool, const char *path, const char *to)
{
        int ret, clk;
        chkid_t chkid;
        char buf[MAX_BUF_LEN], _vfm[MAX_BUF_LEN];
        vfm_t *vfm;

retry:
        printf("That dangerous, are you sure what are you doing? (Yes/No)");

        ret = scanf("%s", buf);
        if (ret != 1) {
                ret = EINVAL;
                goto retry;
        }

        if (strcasecmp(buf, "No") == 0) {
                ret = EPERM;
                GOTO(err_ret, ret);
        } else if (strcasecmp(buf, "Yes") == 0) {
                //pass
        } else {
                goto retry;
        }

        ret = md_chunk_getid(pool, path, &chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        char name0[MAX_NAME_LEN], name1[MAX_NAME_LEN], name2[MAX_NAME_LEN],
                name3[MAX_NAME_LEN], *name[4] = {name0, name1, name2, name3};
        ret = sscanf(to, "%d:%[^,],%[^,],%[^,],%[^,]",
                     &clk, name0, name1, name2, name3);
        if (ret < 1) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        vfm = (void *)_vfm;
        vfm->count = ret - 1;
        vfm->clock = clk;
        DBUG("count %d, clock %d, name %s,%s,%s,%s\n", ret, clk, name[0], name[1], name[2], name[3]);

#if 0
        char name[MAX_NAME_LEN][4];
        ret = sscanf(to, "%d:%[^,],%[^,],%[^,],%[^,]",
                     &clk, name[0], name[1], name[2], name[3]);
        if (ret < 1) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        DWARN("count %d, clock %d, name %s,%s,%s,%s\n", ret, clk, name[0], name[1], name[2], name[3]);
#endif
        
        for (int i = 0; i < vfm->count; i++) {
                ret = maping_host2nid(name[i], &vfm->array[i]);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        
        ret = __lich_vfm_set__(&chkid, vfm);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __lich_vfm_get_chunk(const volid_t *volid, const chkid_t *tid, const chkid_t *chkid)
{
        int ret;
        char buf[MAX_BUF_LEN], _vfm[MAX_BUF_LEN];
        vfm_t *vfm;
        
        vfm = (void *)_vfm;
        ret = md_vfm_get(volid, chkid, vfm);
        if (unlikely(ret)) {
                if (ret == ENOENT) {
                        printf(CHKID_FORMAT" vfm null\n", CHKID_ARG(tid));
                        return 0;
                } else
                        GOTO(err_ret, ret);
        }

        vfm_dump(vfm, buf);
        printf(CHKID_FORMAT" vfm %s\n", CHKID_ARG(tid), buf);

        return 0;
err_ret:
        return ret;
}

static int __lich_vfm_get(const char *pool, const char *path)
{
        int ret, i;
        chkid_t chkid, volid, tid, _chkid;
        fileinfo_t fileinfo;
        uint32_t max, chknum;

        ret = md_chunk_getid(pool, path, &chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (chkid.type == __VOLUME_CHUNK__) {
                volid = chkid;
                
                ret = md_getattr(&volid, &fileinfo);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                chknum = size2chknum(fileinfo.size, NULL);
                max = chknum2tabnum(chknum);

                for (i = 0; i < max; i++) {
                        tid = volid;
                        tid.idx = i;
                        tid.type = __VOLUME_SUB_CHUNK__;

                        tid2cid(&_chkid, &tid, 0);

                        ret = __lich_vfm_get_chunk(&volid, &tid, &_chkid);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                }
        } else if (chkid.type == __RAW_CHUNK__) {
                cid2tid(&tid, &chkid);
                cid2fid(&volid, &chkid);

                ret = __lich_vfm_get_chunk(&volid, &tid, &chkid);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else if (chkid.type == __VOLUME_SUB_CHUNK__) {
                tid = chkid;
                cid2fid(&volid, &chkid);

                tid2cid(&_chkid, &tid, 0);

                ret = __lich_vfm_get_chunk(&volid, &tid, &_chkid);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_disconnect(const char *pool, const char *path, const char *hostname, const char *addr)
{
        int ret;
        chkid_t chkid;
        nid_t peer;

        ret = md_chunk_getid(pool, path, &chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (chkid.type == __VOLUME_CHUNK__) {
                ret = maping_host2nid(hostname, &peer);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                ret = md_disconnect(&chkid, &peer, addr);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_move_md(const char *pool, const fileid_t *fileid, const nid_t *dest)
{
        int ret, idx, i;
        chkinfo_t *chkinfo;
        char buf[MAX_BUF_LEN], str[MAX_BUF_LEN];
        nid_t nid[LICH_REPLICA_MAX * 2];

        chkinfo = (void *)buf;
        ret = md_chunk_getinfo(pool, NULL, fileid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        idx = -1;
        for (i = 0; i < chkinfo->repnum; i++) {
                if (nid_cmp(&chkinfo->diskid[i].id, dest) == 0) {
                        idx = i;
                }

                nid[i] = chkinfo->diskid[i].id;
        }

        if (idx == -1 || idx == 0) {
                nid[0] = *dest;
        } else {
                nid[idx] = nid[0];
                nid[0] = *dest;
        }

        metadata_check_diskid(nid, chkinfo->repnum);

        ret = md_move(&chkinfo->diskid[0].id, fileid, nid, chkinfo->repnum);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = md_chunk_getinfo(pool, NULL, fileid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        CHKINFO_STR(chkinfo, str);
        printf("%s\n", str);

        return 0;
err_ret:
        return ret;
}

static int __lich_move_raw(const char *pool, const nid_t *nid, const fileid_t *fileid)
{
        int ret, chknum, i, retry = 0;
        chkinfo_t *_chkinfo;
        fileid_t chkid;
        fileinfo_t fileinfo;
        char tmp[MAX_BUF_LEN], str[MAX_BUF_LEN];

retry:
        ret = md_getattr(fileid, &fileinfo);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        _chkinfo = (void *)tmp;
        chknum = size2chknum(fileinfo.size, &fileinfo.ec);
        for (i = 0; i < chknum; i++) {
                retry = 0;
retry1:
                ret = md_localize(nid, fileid, i);
                if (unlikely(ret)) {
                        if (ret == ENOENT) {
                                continue;
                        } else if (ret == EAGAIN) {
                                USLEEP_RETRY(err_ret, ret, retry1, retry, 50, (100 * 1000));
                        } else
                                GOTO(err_ret, ret);
                }

                fid2cid(&chkid, fileid, i);
retry2:
                ret = md_chunk_getinfo(pool, NULL, &chkid, _chkinfo, NULL);
                if (unlikely(ret)) {
                        if (ret == EAGAIN) {
                                USLEEP_RETRY(err_ret, ret, retry2, retry, 50, (100 * 1000));
                        } else {
                                GOTO(err_ret, ret);
                        }
                }

                CHKINFO_STR(_chkinfo, str);
                printf("%s\n", str);
        }

        return 0;
err_ret:
        return ret;
}

/** move first replica (controller) to node
 *
 * @param path
 * @param node
 * @param async
 * @return
 */
static int __lich_move(const char *pool, const char *path, const char *node, int async)
{
        int ret, retry = 0;
        nid_t nid;
        fileid_t fileid;

        (void) async;

        printf("move %s to %s\n", path, node);

        ret = md_chunk_getid(pool, path, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (fileid.type != __VOLUME_CHUNK__) {
                EXIT(EINVAL);
        }

        ret = network_connect_byname(node, &nid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

retry:
        ret = __lich_move_md(pool, &fileid, &nid);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        ret = __lich_move_raw(pool, &nid, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __lich_allocate(const char *pool, const char *path, int multi, int fill)
{
        return utils_allocate(pool, path, ALLOCATE_PIPE_DEFAULT, multi, fill);
}

static int __lich_truncate(const char *pool, const char *path, const char *value)
{
        int ret;
        size_t size = 0;

        if (!value) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        ret = utils_get_size(value, &size);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = utils_truncate(pool ? pool : "default", path, size);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        printf("truncate %s to %s\n", path, value);

        return 0;
err_ret:
        return ret;
}

static int __lich_stat(const char *pool, const char *path, int full, int verbose)
{
        int ret, multi = 1;
        fileid_t fileid;
        chkinfo_t *chkinfo;
        char _chkinfo[CHKINFO_MAX];
        filestat_t filestat;

        (void) verbose;

        ret = md_chunk_getid(pool, path, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        chkinfo = (void *)_chkinfo;
        ret = md_chunk_getinfo(pool, NULL, &fileid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_stat_multi(&chkinfo->diskid[0].id, &fileid, &filestat, multi, full);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        utils_info_print(&fileid, &chkinfo->diskid[0].id, &filestat, full);
        
        return 0;
err_ret:
        return ret;
}

static int __lookup_cmp(const char *pool, chkinfo_t *chkinfo, const fileid_t *findid, const char *path)
{
        fileid_t *fileid;
        char info[MAX_BUF_LEN];
        int i;
        const char *stat;
        const reploc_t *diskid;

        fileid = &chkinfo->id;
        if (!chkid_cmp(fileid, findid)) {
                __lookup_find = 1;

                info[0] = '\0';
                for (i = 0; i < (int)chkinfo->repnum; ++i) {
                        diskid = &chkinfo->diskid[i];
                        network_connect1(&diskid->id);
                        if (netable_connected(&diskid->id) == 0) {
                                stat = "offline";
                        } else if (diskid->status == __S_DIRTY) {
                                stat = "dirty";
                        } else if (diskid->status == __S_CHECK) {
                                stat = "check";
                        } else {
                                stat = "clean";
                        }
                        snprintf(info + strlen(info), MAX_NAME_LEN, "%s:%s",
                                 network_rname(&diskid->id), stat);
                        if (i != (int)chkinfo->repnum - 1)
                                strcat(info, ", ");
                }

                printf("chkid : %s\n", id2str(&chkinfo->id));
                printf("path : /%s%s\n", pool, path);
                printf("infover : %llu\n", (LLU)chkinfo->info_version);
                printf("repnum : %d\n", chkinfo->repnum);
                printf("chkinfo : [%s]\n", info);

                return 0;
        }

        return 1;
}

static int __lookup_file(const char *pool, const fileid_t *fileid, const fileid_t *findid, const char *_path)
{
        int ret, delen, retry = 0;
        char buf[MAX_BUF_LEN], path[MAX_PATH_LEN];
        char de0[BIG_BUF_LEN];
        chkinfo_t *chkinfo;
        struct dirent *de;
        uint64_t offset;
        char uuid[MAX_NAME_LEN] = {};

        get_uuid(uuid);

        char snap_name[MAX_NAME_LEN];
        long snap_ver, snap_from;

retry:
        delen = BIG_BUF_LEN;
        ret = stor_snapshot_list(fileid, uuid, 0, de0, &delen);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        if (delen == 0)
                goto out;

        dir_for_each(de0, delen, de, offset) {
                if (strlen(de->d_name) == 0) {
                        break;
                }

                if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
                        continue;
                }

                if (__lookup_find)
                        goto out;

                ret = sscanf(de->d_name, "%s %ld %ld", snap_name, &snap_ver, &snap_from);
                if (ret != 3)
                        continue;

                chkinfo = (void *)buf;
                ret = md_lookup_byname(pool, fileid, snap_name, chkinfo, NULL);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                sprintf(path, "%s@%s", _path, snap_name);

                if (!__lookup_cmp(pool, chkinfo, findid, path))
                        goto out;

        }

out:
        return 0;
err_ret:
        return ret;
}

static int __lookup_dir(const char *pool, const fileid_t *fileid, const fileid_t *findid, const char *_path)
{
        int ret, delen, retry = 0;
        struct dirent *de;
        char buf[MAX_BUF_LEN], de0[BIG_BUF_LEN], path[MAX_PATH_LEN];
        uint64_t offset = 0, offset2 = 0;
        chkinfo_t *chkinfo;
        char uuid[MAX_NAME_LEN] = {};

        YASSERT(fileid->type == __POOL_CHUNK__);

        get_uuid(uuid);
        ret = md_listpool_open(fileid, uuid);
        if (ret)
                GOTO(err_ret, ret);

        while (1) {
retry:
                memset(de0, 0, BIG_BUF_LEN);
                delen = BIG_BUF_LEN;
                ret = md_listpool(fileid, uuid, offset, de0, &delen);
                if (unlikely(ret)) {
                        if (ret == EAGAIN) {
                                USLEEP_RETRY(err_close, ret, retry, retry, 50, (100 * 1000));
                        } else
                                GOTO(err_close, ret);
                }

                if (delen == 0)
                        break;

                offset2 = 0;
                dir_for_each(de0, delen, de, offset2) {
                        if (strlen(de->d_name) == 0) {
                                goto out;
                        } else if (delen - offset2 < sizeof(*de) + MAX_NAME_LEN)
                                break;

                        offset += de->d_reclen;

                        if (strcmp(de->d_name, ".") == 0
                                        || strcmp(de->d_name, "..") == 0)
                                continue;

                        if (__lookup_find)
                                goto out;

                        chkinfo = (void *)buf;
                        ret = md_lookup_byname(pool, fileid, de->d_name, chkinfo, NULL);
                        if (unlikely(ret))
                                GOTO(err_close, ret);

                        if (!strcmp(_path, "/"))
                                sprintf(path, "/%s", de->d_name);
                        else
                                sprintf(path, "%s/%s", _path, de->d_name);

                        if (chkinfo->id.type == __VOLUME_CHUNK__) {
                                if (!__lookup_cmp(pool, chkinfo, findid, path))
                                        goto out;

                                if (!chkid_cmp(&unlink_fid, fileid))
                                        continue;

                                ret = __lookup_file(pool, &chkinfo->id, findid, path);
                                if (unlikely(ret))
                                        GOTO(err_close, ret);
                        } else {
                                if (!__lookup_cmp(pool, chkinfo, findid, path))
                                        goto out;

                                ret = __lookup_dir(pool, &chkinfo->id, findid, path);
                                if (unlikely(ret))
                                        GOTO(err_close, ret);
                        }
                }
        }

out:
        md_listpool_close(fileid, uuid);
        return 0;
err_close:
        md_listpool_close(fileid, uuid);
err_ret:
        return ret;
}

static int __lich_lookup(void *_pool, void *_name)
{
        int ret;
        fileid_t rootid;
        fileid_t findid;
        chkinfo_t *chkinfo;
        char buf[MAX_BUF_LEN];
        char *pool = _pool, *name = _name;

        ret = md_chunk_getid(pool, name, &findid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_lookup1(pool, "/", &rootid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_lookup1(pool, UNLINK_ROOT, &unlink_fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        chkinfo = (void *)buf;
        ret = md_chunk_getinfo(pool, NULL, &rootid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (!__lookup_cmp(pool, chkinfo, &findid, "/"))
                goto out;

        ret = __lookup_dir(pool, &rootid, &findid, "/");
        if (unlikely(ret))
                GOTO(err_ret, ret);
#if 0
        if (!__lookup_find)
                printf("not found!\n");
#endif
out:
        return 0;
err_ret:
        return ret;
}

static int __lich_md5sum(const char *pool, const char *name)
{
        int ret;
        fileid_t fileid;

        ret = md_chunk_getid(pool, name, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = file_md5sum(pool, &fileid, name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __lich_chunk_move(const char *pool, const char *path, const char *to)
{
        int ret, dist_count, i;
        char tmp[MAX_BUF_LEN];
        char *tmp2[LICH_REPLICA_MAX];
        nid_t dist[LICH_REPLICA_MAX];
        fileid_t _parent;
        chkid_t _chkid;
        fileid_t *parent = &_parent;
        chkid_t *chkid = &_chkid;
        chkinfo_t *chkinfo, *parent_chkinfo;
        char buf[CHKINFO_MAX];
        char buf2[CHKINFO_MAX];
        chkinfo = (void *)buf;
        parent_chkinfo = (void *)buf2;
        nid_t nid;
        fileid_t srv;

        if (strlen(path) == 0) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        ret = md_chunk_getid(pool, path, chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        strcpy(tmp, to);
        dist_count = LICH_REPLICA_MAX;
        _str_split(tmp, ',', tmp2, &dist_count);

        for (i = 0; i < dist_count; i++) {
                ret = network_connect_byname(tmp2[i], &dist[i]);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        ret = md_parent_get(chkid, parent);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = md_chunk_getinfo(pool, parent, chkid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (chkid->type == __POOL_CHUNK__ || chkid->type == __VOLUME_CHUNK__) {
                nid = chkinfo->diskid[0].id;
                srv = chkinfo->id;
        } else {
                ret = md_chunk_getinfo(pool, NULL, parent, parent_chkinfo, NULL);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                nid = parent_chkinfo->diskid[0].id;
                srv = *parent;
        }

        ret = md_chunk_move(&nid, &srv, chkid, (const nid_t *)dist, dist_count);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __lich_chunk_stat(const char *pool, const char *path)
{
        int ret;
        fileid_t _parent;
        chkid_t _chkid;
        fileid_t *parent = &_parent;
        chkid_t *chkid= &_chkid;
        chkinfo_t *chkinfo, *parent_chkinfo;
        char buf[CHKINFO_MAX], buf2[CHKINFO_MAX], buf3[MAX_BUF_LEN];
        chkinfo = (void *)buf;
        parent_chkinfo = (void *)buf2;

        if (strlen(path) == 0) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        ret = md_chunk_getid(pool, path, chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = md_parent_get(chkid, parent);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = md_chunk_getinfo(pool, parent, chkid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (chkid_isroot(parent)) {
                printf("parent: null\n");
        } else {
                ret = md_chunk_getinfo(pool, NULL, parent, parent_chkinfo, NULL);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                CHKINFO_STR(parent_chkinfo, buf3);
                printf("parent: %s\n", buf3);
        }
        CHKINFO_STR(chkinfo, buf3);
        printf("chkinfo: %s\n", buf3);

        return 0;
err_ret:
        return ret;
}

static int __db_dump__(int db_idx, const char *path, char *sql, int *count, int *disk, int *loc)
{
        int ret, row, column, i, r, slen;
        sqlite3 *db;
        char *zErrMsg = NULL, **result;
        char key[MAX_BUF_LEN], _parent[MAX_BUF_LEN];
        chkid_t *chkid, *parent;

        ret = sqlite3_open(path, &db);
        if (ret ){
                DERROR("SQL error: path %s\n", path);
                ret = EIO;
                GOTO(err_ret, ret);
        }

        ret = sqlite3_get_table(db, sql, &result, &row, &column, &zErrMsg);
        if (ret != SQLITE_OK ){
                DERROR("SQL error: %s\n", zErrMsg);
                SERROR(0, "%s SQL error: %s\n", M_DISK_META_ERROR, zErrMsg);
                GOTO(err_close, ret);
        }

        r = column;
        for (i = 0; i < row; i++) {
                base64_decode(result[r], &slen, key);
                chkid = (void *)key;

                base64_decode(result[r+3], &slen, _parent);
                parent = (void *)_parent;

                printf("%4d %8d "CHKID_FORMAT", "CHKID_FORMAT", %s, %s, %s, %s, %s, %s, %s\n",
                       db_idx, i, CHKID_ARG(parent), CHKID_ARG(chkid),
                       result[r+1], result[r+2], result[r+4], result[r+5],
                       result[r+6], result[r+7], result[r+8]);
                if (count)
                        *count = *count + 1;
                if (disk)
                        *disk = atoi(result[r+1]);
                if (loc)
                        *loc = atoi(result[r+2]);
                r = r + column;
        }
        /*printf("column: %d, row: %d\n", column, row);*/

        sqlite3_free_table(result);
        sqlite3_free(zErrMsg);

        ret = sqlite3_close(db);
        if (ret)
                GOTO(err_ret, ret);

        return 0;
err_close:
        YASSERT(sqlite3_close(db));
err_ret:
        return ret;
}

static int __db_mode__(const char *idx, const char *key, char *value)
{
        int ret, row, column;
        sqlite3 *db;
        char *zErrMsg = NULL, **result;
        char sql[MAX_BUF_LEN], path[MAX_BUF_LEN];

        sprintf(path, "%s/data/chunk/%s.db", gloconf.home, idx);
        ret = sqlite3_open(path, &db);
        if (ret ){
                ret = EIO;
                GOTO(err_ret, ret);
        }

        snprintf(sql, MAX_BUF_LEN, "PRAGMA %s", key);
        ret = sqlite3_get_table(db, sql, &result, &row, &column, &zErrMsg);
        if (ret != SQLITE_OK ) {
                DERROR("SQL error: %s\n", zErrMsg);
                SERROR(0, "%s SQL error: %s\n", M_DISK_META_ERROR, zErrMsg);
                GOTO(err_close, ret);
        }

        fprintf(stderr, "disk %-2s %s : %s\n", idx, key, result[1]);

        if (value) {
                strcpy(value, result[1]);
        }

        sqlite3_free_table(result);
        sqlite3_free(zErrMsg);

        ret = sqlite3_close(db);
        if (ret)
                GOTO(err_ret, ret);

        return 0;
err_close:
        YASSERT(sqlite3_close(db));
err_ret:
        return ret;
}

#define __DB_CHUNK_COUNT__ 10

/*临时用来查数据库用*/
static int __lich_db_dump(const char *idx, const char *name)
{
        int ret, i;
        char sql[MAX_BUF_LEN], path[MAX_PATH_LEN];
        if (!strcmp(idx, "all")) {
                if (!name || (name && !strcmp(name, "metadata"))) {
                        sprintf(sql, "select * from metadata");
                        for (i = 0; i < __DB_CHUNK_COUNT__; i++) {
                                sprintf(path, "%s/data/chunk/%d.db", gloconf.home, i);
                                __db_dump__(i, path, sql, NULL, NULL, NULL);
                        }
                }

                if (!name || (name && !strcmp(name, "raw"))) {
                        sprintf(sql, "select * from raw");
                        for (i = 0; i < __DB_CHUNK_COUNT__; i++) {
                                sprintf(path, "%s/data/chunk/%d.db", gloconf.home, i);
                                __db_dump__(i, path, sql, NULL, NULL, NULL);
                        }
                }
        } else if (strlen(idx) == 1 && isdigit(*idx) && atoi(idx) >= 0 && atoi(idx) < __DB_CHUNK_COUNT__) {
                sprintf(path, "%s/data/chunk/%s.db", gloconf.home, idx);

                if (!name || (name && !strcmp(name, "metadata"))) {
                        sprintf(sql, "select * from metadata");
                        __db_dump__(atoi(idx), path, sql, NULL, NULL, NULL);
                }

                if (!name || (name && !strcmp(name, "raw"))) {
                        sprintf(sql, "select * from raw");
                        __db_dump__(atoi(idx), path, sql, NULL, NULL, NULL);
                }
        } else {
                DERROR("%s/data/chunk/%s.db not found!\n", gloconf.home, idx);
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_db_mode_dump(const char *idx)
{
        int ret, i;
        char _idx[MAX_PATH_LEN];

        if (!strcmp(idx, "all")) {
                for (i = 0; i < __DB_CHUNK_COUNT__; i++) {
                        sprintf(_idx, "%d", i);
                        __db_mode__(_idx, "main.journal_mode", NULL);
                }
        } else if (strlen(idx) == 1 && isdigit(*idx) && atoi(idx) >= 0 && atoi(idx) < __DB_CHUNK_COUNT__) {
                __db_mode__(idx, "main.journal_mode", NULL);
        } else {
                DERROR("%s/data/chunk/%s.db not found!\n", gloconf.home, idx)
                        ret = EINVAL;
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}
static int __diskmd_real_path(char *path, struct stat* stbuf)
{
        int ret;
        char buf[MAX_PATH_LEN];
        char *tmp;

        ret = lstat(path, stbuf);
        if (ret < 0) {
                ret = errno;
                goto err_ret;
        }

        while (S_ISLNK(stbuf->st_mode)) {
                memset(buf, 0, sizeof(buf));

                ret = readlink(path, buf, sizeof(buf));
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }

                tmp = realpath(buf, path);
                (void) tmp;

                ret = lstat(path, stbuf);
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }
        }

        return 0;
err_ret:
        return ret;
}

static void __db_select(const fileid_t *fileid, int *count, int *disk, int *loc)
{
        int i;
        char sql[MAX_BUF_LEN], key[MAX_BUF_LEN], path[MAX_PATH_LEN];

        base64_encode((void *)fileid, sizeof(*fileid), key);
        *count = 0;

        sprintf(sql, "select * from metadata where key='%s'", key);
        for (i = 0; i < __DB_CHUNK_COUNT__; i++) {
                sprintf(path, "%s/data/chunk/%d.db", gloconf.home, i);
                __db_dump__(i, path, sql, count, disk, loc);
        }

        sprintf(sql, "select * from raw where key='%s'", key);
        for (i = 0; i < __DB_CHUNK_COUNT__; i++) {
                sprintf(path, "%s/data/chunk/%d.db", gloconf.home, i);
                __db_dump__(i, path, sql, count, disk, loc);
        }

        YASSERT(*count <= 1);
}

static int __lich_chunk_dump(const char *pool, const char *path, const char *dst)
{
        int ret, found, disk, loc, fd;
        fileid_t fileid;
        char diskpath[PATH_MAX], buf[LICH_CHUNK_SPLIT];
        struct stat stbuf;

        strcpy(gloconf.home, "/opt/fusionstack");

        ret = md_chunk_getid(pool, path, &fileid);
        if (ret)
                GOTO(err_ret, ret);

        __db_select(&fileid, &found, &disk, &loc);

        if (found) {
                sprintf(diskpath, "%s/data/disk/disk/%d.disk", gloconf.home, disk);

                ret = __diskmd_real_path(diskpath, &stbuf);
                if (ret) {
                        GOTO(err_ret, ret);
                }

                fd = open(diskpath, O_RDWR | O_SYNC, 0);
                if (fd < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }

                ret = pread(fd, buf, LICH_CHUNK_SPLIT, loc * LICH_CHUNK_SPLIT);
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_fd, ret);
                }

                close(fd);

                if (dst)
                        snprintf(diskpath, PATH_MAX, "%s", dst);
                else
                        sprintf(diskpath, ""CHKID_FORMAT".dump", CHKID_ARG(&fileid));

                fd = open(diskpath, O_CREAT | O_RDWR | O_SYNC, 0644);
                if (fd < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }

                printf("dump chunk "CHKID_FORMAT" to %s\n", CHKID_ARG(&fileid), diskpath);

                ret = write(fd, buf, LICH_CHUNK_SPLIT);
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_fd, ret);
                }

                close(fd);
        } else {
                printf("chkid "CHKID_FORMAT" not found!\n", CHKID_ARG(&fileid));
        }

        return 0;
err_fd:
        close(fd);
err_ret:
        return ret;
}

static int __lich_chunk_zerofill(const char *pool, const char *path)
{
        int ret, found, disk, loc, fd;
        fileid_t fileid;
        char diskpath[PATH_MAX], buf[LICH_CHUNK_SPLIT];
        struct stat stbuf;


        ret = md_chunk_getid(pool, path, &fileid);
        if (ret)
                GOTO(err_ret, ret);

        __db_select(&fileid, &found, &disk, &loc);

        if (found) {
                printf("fill zero to chunk "CHKID_FORMAT"\n", CHKID_ARG(&fileid));

                sprintf(diskpath, "%s/data/disk/disk/%d.disk", gloconf.home, disk);

                ret = __diskmd_real_path(diskpath, &stbuf);
                if (ret) {
                        GOTO(err_ret, ret);
                }

                fd = open(diskpath, O_RDWR | O_SYNC, 0);
                if (fd < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }

                memset(buf, 0x0, sizeof(buf));

                ret = pwrite(fd, buf, LICH_CHUNK_SPLIT, loc * LICH_CHUNK_SPLIT);
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_fd, ret);
                }

                close(fd);
        } else {
                printf("chkid "CHKID_FORMAT" not found!\n", CHKID_ARG(&fileid));
        }

        return 0;
err_fd:
        close(fd);
err_ret:
        return ret;
}

static int __lich_chunk_fill(const char *pool, const char *path, const char *src, int fill)
{
        int ret, found, disk, loc, fd;
        fileid_t fileid;
        char diskpath[PATH_MAX], buf[LICH_CHUNK_SPLIT];
        struct stat stbuf;


        ret = md_chunk_getid(pool, path, &fileid);
        if (ret)
                GOTO(err_ret, ret);

        __db_select(&fileid, &found, &disk, &loc);

        if (found) {
                printf("fill %s to chunk "CHKID_FORMAT"\n", src, CHKID_ARG(&fileid));

                fd = open(src, O_CREAT | O_RDWR | O_SYNC, 0644);
                if (fd < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }

                ret = read(fd, buf, LICH_CHUNK_SPLIT);
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_fd, ret);
                }

                close(fd);

                sprintf(diskpath, "%s/data/disk/disk/%d.disk", gloconf.home, disk);

                ret = __diskmd_real_path(diskpath, &stbuf);
                if (ret) {
                        GOTO(err_ret, ret);
                }

                fd = open(diskpath, O_RDWR | O_SYNC, 0);
                if (fd < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }

                ret = pwrite(fd, buf, LICH_CHUNK_SPLIT, loc * LICH_CHUNK_SPLIT);
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_fd, ret);
                }

                close(fd);
        } else {
                if (fill) {
                        char hostname[MAX_NAME_LEN];
                        fileid_t parent;
                        nid_t nid;
                        buffer_t data;

                        ret = net_gethostname(hostname, MAX_NAME_LEN);
                        if (unlikely(ret)) {
                                if (ret == ECONNREFUSED)
                                        strcpy(hostname, "N/A");
                                else
                                        GOTO(err_ret, ret);
                        }

                        ret = network_connect_byname(hostname, &nid);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);

                        ret = md_parent_get(&fileid, &parent);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);

                        fd = open(src, O_CREAT | O_RDWR | O_SYNC, 0644);
                        if (fd < 0) {
                                ret = errno;
                                GOTO(err_ret, ret);
                        }

                        ret = read(fd, buf, LICH_CHUNK_SPLIT);
                        if (ret < 0) {
                                ret = errno;
                                GOTO(err_fd, ret);
                        }

                        close(fd);

                        printf("fill chunk "CHKID_FORMAT" to hostname:%s nid:"NID_FORMAT" parent:"CHKID_FORMAT" src:%s\n",
                                        CHKID_ARG(&fileid), hostname, NID_ARG(&nid), CHKID_ARG(&parent), src);

                        mbuffer_init(&data, 0);
                        mbuffer_copy(&data, buf, LICH_CHUNK_SPLIT);

                        ret = chunk_push_with(pool, &fileid, &data, &nid, 0, 0, &parent, -1, 0);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        printf("chkid "CHKID_FORMAT" not found!\n", CHKID_ARG(&fileid));
                }
        }

        return 0;
err_fd:
        close(fd);
err_ret:
        return ret;
}

static int __lich_chunk_cleanup(const char *pool, const char *path)
{
        int ret, retry = 0;
        char name[MAX_NAME_LEN];
        fileid_t parent;
        nid_t nid;

retry0:
        ret = stor_splitpath(pool, path, &parent, name);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry0, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        ret = md_map_getsrv(&parent, &nid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (net_islocal(&nid)) {
                ret = stor_ctl_cleanup(&parent, name);
                if (unlikely(ret)) {
                        if (ret == ENOENT) {
                                DWARN("cleanup %s @ "CHKID_FORMAT" not found\n",
                                      name, CHKID_ARG(&parent));
                        } else
                                GOTO(err_ret, ret);
                }
        } else {
                ret = stor_rpc_cleanup(&nid, &parent, name);
                if (unlikely(ret)) {
                        if (ret == ENOENT) {
                                DWARN("cleanup %s @ "CHKID_FORMAT" not found\n",
                                      name, CHKID_ARG(&parent));
                        } else
                                GOTO(err_ret, ret);
                }
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_base64_encode(const char *pool, const char *name, int pithy)
{
        int ret;
        fileid_t fileid;
        char key[MAX_BUF_LEN];

        if (pithy) {
                base64_encode((void *)name, strlen(name), key);
                printf("encode: %s\n", key);
        } else {
                ret = md_chunk_getid(pool, name, &fileid);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                base64_encode((void *)&fileid, sizeof(fileid), key);
                printf("chkid: "CHKID_FORMAT"\n", CHKID_ARG(&fileid));
                printf("encode: %s\n", key);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_base64_decode(const char *pool, const char *name, int pithy)
{
        int slen, i;
        char key[MAX_BUF_LEN];
        chkid_t *chkid;

        (void) pool;
        if (pithy) {
                base64_decode(name, &slen, key);
                printf("decode: %s\n   HEX:", key);
                for (i=0; i<slen; i++) {
                        printf("%02x ", *((unsigned char *)key + i));
                }
                printf("(%d)\n", slen);
        } else {
                base64_decode(name, &slen, key);
                chkid = (void *)key;

                if (chkid->type > __RAW_CHUNK__ || chkid->type < __NULL_CHUNK__)
                        return EINVAL;

                printf("chkid: "CHKID_FORMAT"\n", CHKID_ARG(chkid));
        }

        return 0;
}

static int __lich_crc32(const char *name)
{
        int ret, fd, size;
        struct stat stbuf;
        char *buf;
        uint64_t left, offset;
        uint32_t crcode, crc;

        ret = stat(name, &stbuf);
        if (ret < 0) {
                ret = errno;
                GOTO(err_ret, ret);
        }

        fd = open(name, O_RDONLY);
        if (fd < 0) {
                ret = errno;
                GOTO(err_ret, ret);
        }

        ret = ymalloc((void *)&buf, LICH_CHUNK_SPLIT);
        if (unlikely(ret))
                GOTO(err_close, ret);

        crc32_init(crcode);
        offset = 0;
        left = stbuf.st_size;

        while (left > 0) {
                size = _min(left, LICH_CHUNK_SPLIT);

                ret = pread(fd, buf, size, offset);
                if (ret != size) {
                        if (ret < 0) {
                                ret = errno;
                        } else {
                                ret = EIO;
                        }

                        GOTO(err_free, ret);
                }

                crc32_stream(&crcode, buf, size);

                offset += size;
                left -= size;
        }

        crc = crc32_stream_finish(crcode);

        close(fd);

        printf("path:%s crc32:%x\n", name, crc);
        yfree((void **)&buf);

        return 0;
err_free:
        yfree((void **)&buf);
err_close:
        close(fd);
err_ret:
        return ret;
}

static int __lich_connection(const char *pool, const char *path)
{
        int ret, count = MAX_BUF_LEN, i, left;
        fileid_t fileid;
        chkinfo_t *chkinfo;
        char _chkinfo[CHKINFO_MAX];
        char _buf[MAX_BUF_LEN], tmp[MAX_BUF_LEN], buf[MAX_BUF_LEN];
        const nid_t *nid;
        const uint32_t *addrs;
        uint32_t size;
        const void *ptr;

        ret = md_chunk_getid(pool, path, &fileid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if(fileid.type != __VOLUME_CHUNK__){
                printf("Please input valid path: \n    such as: /iscsi/pool/1\n");
                GOTO(err_ret, ret);
        }

        chkinfo = (void *)_chkinfo;
        ret = md_chunk_getinfo(pool, NULL, &fileid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        memset(buf, 0x00, MAX_BUF_LEN);
        memset(_buf, 0x00, MAX_BUF_LEN);
        ret = md_connection(&chkinfo->diskid[0].id, &fileid, _buf, &count);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (memcmp(_buf, buf, MAX_BUF_LEN)) {
                buf[0] = '\0';
                ptr = _buf;
                while (ptr - (void *)_buf < count) {
                        left = count - (ptr - (void *)_buf);
                        ptr = _opaque_decode(ptr, left, &nid, NULL, &addrs, &size, NULL);

                        tmp[0] = '\0';
                        for (i = 0; i < (int)(size / sizeof(*addrs)); i++) {
                                if (i == 0)
                                        snprintf(tmp, MAX_BUF_LEN, "%s", _inet_ntoa(addrs[i]));
                                else
                                        snprintf(tmp + strlen(tmp), MAX_BUF_LEN, ",%s", _inet_ntoa(addrs[i]));
                        }

                        snprintf(buf + strlen(buf), MAX_BUF_LEN, "%s:%s\n", netable_rname_nid(nid), tmp);
                }
        }

        printf("%s\n", buf);

        return 0;
err_ret:
        return ret;
}

static int __lich_boardcast(const char*name)
{
        int ret;
        chkid_t chkid;

        (void) name;

        printf("boardcast: %s\n", name);

        str2chkid(&chkid, name);
        ret = locator_rpc_boardcast(&chkid);
        if (ret)
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __confirm()
{
        char confirm[MAX_NAME_LEN];

        if (fgets(confirm, MAX_NAME_LEN, stdin) != NULL) {
                if ((strlen(confirm) - 1 == strlen("yes"))
                        && (strncmp(confirm, "yes", strlen("yes")) == 0)) {
                        return 0;
                } else {
                        return EPERM;
                }
        } else {
                return EPERM;
        }

        return 0;
}

static int __lich_rmvol_force(const char *pool, const char *file)
{
        int i, ret, force, retry;
        char name[MAX_NAME_LEN];
        fileid_t parent;

        retry = 0;
retry0:
        ret = stor_splitpath(pool, file, &parent, name);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry0, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        printf("force rmvol pool: %s file: %s\n", pool, file);
        printf("This operation is dangerous, Need 3 confirmation\n");
        for (i = 0; i < 3; i++) {
                printf("Confirmation %d (yes/no):", i + 1);
                ret = __confirm();
                if (ret) {
                        GOTO(err_ret, ret);
                }
        }

        force = 1;
        ret = md_rmvol(&parent, name, force);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __lich_dispatch(const char *pool, const char *site, int repnum)
{
        int ret, i;
        const char *distname;
        nid_t dist[LICH_REPLICA_MAX];

        if (0 < repnum && repnum <= LICH_REPLICA_MAX) {
        } else {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        ret = dispatch_newdisk(dist, &repnum, repnum, pool, NULL, 0, __NEWDISK_BALANCE__);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for (i = 0; i < repnum; i++) {
                distname = network_rname(&dist[i]);
                printf("%s\n", distname);
        }

        return 0;
err_ret:
        return ret;
}

static int __getopt(int argc, char *argv[], char *arg)
{
        int i;

        for (i = 0; i < argc; i++) {
                if (!strcmp(arg, argv[i])) {
                        if (i + 1 < argc)
                                return i + 1;
                        break;
                }
        }

        return -1;
}

int main(int argc, char *argv[])
{
        int ret, op = OP_NULL, alone = 0, verbose = 0, force = 0, thread = 0,
                async = 0, i, pithy = 0, fill = 0, deep = 1, _argc, full = 0,
                parents = 0, priority= -1, repnum, retry = 0;
        char c_opt, cwdpath[PATH_MAX];
        const char *path = NULL, *name = NULL, *to = NULL;
        const char *dispatch_pool=NULL, *dispatch_site=NULL;
        char pool[MAX_PATH_LEN] = {0}, path2[MAX_PATH_LEN] = {0};
        recovery_t recovery;

        char tmp[MAX_NAME_LEN], *dist[2];
        int count = 2;
        ec_t ec;
        uint32_t k = 0, r = 0;

        dbg_info(0);

        (void) async;
        (void) force;
        (void) path;

        while (srv_running) {
                int option_index = 0;

                static struct option long_options[] = {
                        // 0 - 4
                        { "cmp", required_argument, 0, 0},
                        { "recover", required_argument, 0, 0},
                        { "chunkset", required_argument, 0, 0},
                        { "async", no_argument, 0, 0},
                        { "allocate", required_argument, 0, 0},

                        // 5 - 9
                        { "scan", required_argument, 0, 0},
                        { "lookup", required_argument, 0, 0},
                        { "deep", no_argument, 0, 0},
                        { "md5sum", required_argument, 0, 0},
                        { "tier", required_argument, 0, 0},

                        // 10 -14
                        { "priority", required_argument, 0, 0},
                        { "localize", required_argument, 0, 0},
                        { "writeback", required_argument, 0, 0},
                        { "multipath", required_argument, 0, 0},
                        { "chunkmove", required_argument, 0, 0},

                        // 15 - 22
                        { "chunkstat", required_argument, 0, 0},
                        { "dbdump", required_argument, 0, 0},
                        { "dbmode", required_argument, 0, 0},
                        { "base64", required_argument, 0, 0},
                        { "crc32", required_argument, 0, 0},
                        { "chunkdump", required_argument, 0, 0},
                        { "chunkfill", required_argument, 0, 0},
                        { "chunkcleanup", required_argument, 0, 0},

                        // 23 - 32
                        { "boardcast", required_argument, 0, 0},
                        { "readraw", required_argument, 0, 0},
                        { "full", no_argument, 0, 0},
                        { "mkdir", required_argument, 0, 0},
                        { "touch", required_argument, 0, 0},
                        { "unlink", required_argument, 0, 0},
                        { "rmdir", required_argument, 0, 0},

                        { "rmvolforce", required_argument, 0, 0},     // 30
                        { "dispatch", required_argument, 0, 0},
                        { "truncate", required_argument, 0, 0},
                        { "vfmset", required_argument, 0, 0},
                        { "vfmget", required_argument, 0, 0},

                        { "thread", required_argument, 0, 0},        // 35
                        { "disconnect", required_argument, 0, 0},

                        // -- END

                        { "connection", required_argument, 0, 'c'},
                        { "list", required_argument, 0, 'l'},
                        { "chunkinfo", required_argument, 0, 'd'},
                        { "move", required_argument, 0, 'm'},

                        { "stat", required_argument, 0, 's'},
                        { "fill", 0, 0, 'f'},
                        { "pithy", 0, 0, 'p'},
                        { "parents", 0, 0, 'P'},
                        { "verbose", 0, 0, 'v' },
                        { "help",    0, 0, 'h' },

                        { 0, 0, 0, 0 },
                };

                c_opt = getopt_long(argc, argv, "vhl:s:pPefc:qo:m:", long_options, &option_index);
                if (c_opt == -1)
                        break;

                switch (c_opt) {
                case 0:
                        switch (option_index) {
                        case 0:
                                DBUG("cmp\n");
                                op = OP_CMP;
                                path = optarg;
                                break;
                        case 1:
                                DBUG("recover\n");
                                op = OP_RECOVER;
                                path = optarg;
                                break;
                        case 2:
                                op = OP_CHUNKSET;
                                path = optarg;
                                break;
                        case 3:
                                async = 1;
                                break;
                        case 4:
                                op = OP_ALLOCATE;
                                path = optarg;
                                break;
                        case 5:
                                op = OP_SCAN;
                                path = optarg;

                                break;
                        case 6:
                                op = OP_LOOKUP;
                                path = optarg;
                                break;
                        case 7:
                                deep = 1;
                                break;
                        case 8:
                                op = OP_MD5SUM;
                                path = optarg;
                                break;
                        case 9:
                                op = OP_TIER;
                                path = optarg;
                                break;
                        case 10:
                                op = OP_PRIORITY;
                                path = optarg;
                                break;
                        case 11:
                                op = OP_LOCALIZE;
                                path = optarg;
                                break;
                        case 12:
                                op = OP_WRITEBACK;
                                path = optarg;
                                break;
                        case 13:
                                op = OP_MULTPATH;
                                path = optarg;
                                break;
                        case 14:
                                op = OP_CHUNK_MOVE;

                                if (argc < 4) {
                                        usage();
                                        exit(EINVAL);
                                }

                                for (i = 0; i < argc; i++) {
                                        if (optarg == argv[i]) {
                                                path = argv[i];
                                                to = argv[i + 1];
                                        }
                                }
                                break;
                        case 15:
                                op = OP_CHUNK_STAT;
                                path = optarg;
                                break;
                        case 16:
                                op = OP_DB_DUMP;
                                path = optarg;
                                if (argc == 4) {
                                        name = argv[argc - 1];
                                }
                                break;
                        case 17:
                                op = OP_DB_MODE;
                                path = optarg;
                                break;
                        case 18:
                                op = OP_BASE64;
                                name = optarg;
                                break;
                        case 19:
                                op = OP_CRC32;
                                name = optarg;
                                break;
                        case 20:
                                op = OP_CHUNK_DUMP;
                                path = optarg;
                                break;
                        case 21:
                                op = OP_CHUNK_FILL;
                                path = optarg;
                                break;
                        case 22:
                                op = OP_CHUNK_CLEANUP;
                                path = optarg;
                                break;
                        case 23:
                                op = OP_BOARDCAST;
                                path = optarg;
                                break;
                        case 24:
                                op = OP_READRAW;
                                path = optarg;
                                break;
                        case 25:
                                full = VOL_INFO_FULL;
                                break;
                        case 26:
                                op = OP_MKDIR;
                                path = optarg;
                                break;
                        case 27:
                                op = OP_TOUCH;
                                path = optarg;
                                break;
                        case 28:
                                op = OP_UNLINK;
                                path = optarg;
                                break;
                        case 29:
                                op = OP_RMDIR;
                                path = optarg;
                                break;
                        case 30:
                                op = OP_RMVOL_FORCE;
                                path = optarg;
                                break;
                        case 31:
                                op = OP_DISPATCH;
                                if (argc < 5) {
                                        usage();
                                        EXIT(EINVAL);
                                }
                                dispatch_pool = argv[2];
                                dispatch_site = argv[3];
                                repnum = atoi(argv[4]);
                                break;
                        case 32:
                                op = OP_TRUNCATE;
                                if (argc < 4) {
                                        usage();
                                        EXIT(EINVAL);
                                }

                                path = optarg;
                                to = argv[3];

                                break;
                        case 33:
                                op = OP_VFMSET;
                                if (argc < 4) {
                                        usage();
                                        EXIT(EINVAL);
                                }

                                path = optarg;
                                to = argv[3];

                                break;
                        case 34:
                                op = OP_VFMGET;
                                if (argc < 3) {
                                        usage();
                                        EXIT(EINVAL);
                                }

                                path = optarg;

                                break;
                        case 35:
                                if (argc < 3) {
                                        usage();
                                        EXIT(EINVAL);
                                }

                                thread = atoi(optarg);
                                //printf("thread %s %u\n", optarg, thread);

                                break;
                        case 36:  // disconnect
                                if (argc < 5) {
                                        usage();
                                        EXIT(EINVAL);
                                }

                                op = OP_DISCONNECT;
                                path = optarg;
                                break;
                        default:
                                fprintf(stderr, "Hoops, wrong op got!\n");
                                YASSERT(0);
                        }

                        break;
                case 'l':
                        DBUG("list\n");
                        op = OP_LIST;
                        path = optarg;
                        break;
                case 's':
                        DBUG("stat\n");
                        op = OP_STAT;
                        path = optarg;
                        break;
                case 'm':
                        DBUG("move %s\n", argv[1]);
                        op = OP_MOVE;

                        //args = (void *)optarg;

                        if (argc < 4) {
                                usage();
                                EXIT(EINVAL);
                        }

                        for (i = 0; i < argc; i++) {
                                if (optarg == argv[i]) {
                                        path = argv[i];
                                        to = argv[i + 1];
                                }
                        }

                        break;
                case 'b':
                        op = OP_BLANCEOBJ;
                        path = optarg;
                        break;
                case 'd':
                        DBUG("stat\n");
                        op = OP_CHUNK_INFO;
                        path = optarg;
                        break;
                case 'c':
                        DBUG("connection\n");
                        op = OP_CONNECTION;
                        path = optarg;
                        break;
                case 'p':
                        pithy = 1;
                        break;
                case 'P':
                        parents = 1;
                        int idx = __getopt(argc, argv, "-P");
                        if (idx >= 0) {
                                if (!is_zero_one_char(argv[idx])) {
                                        usage();
                                        EXIT(EINVAL);
                                }
                                priority = atoi(argv[idx]);
                        }
                        break;
                case 'e':
                        strcpy(tmp, optarg);
                        _str_split(tmp, '+', dist, &count);
                        if (count != 2) {
                                usage();
                                EXIT(EINVAL);
                        }

                        if (!_str_isdigit(dist[0]) || !_str_isdigit(dist[1])) {
                                usage();
                                EXIT(EINVAL);
                        }

                        ret = sscanf(optarg, "%u+%u", &k, &r);
                        if (ret != 2 || !k || !r) {
                                usage();
                                EXIT(EINVAL);
                        }

                        break;
                case 'f':
                        fill = 1;
                        break;
                case 'v':
                        verbose = 1;
                        break;
                case 'h':
                        usage();
                        EXIT(0);
                default:
                        usage();
                        EXIT(EINVAL);
                }
        }

#if 0
        {
                printf("op %d\n", op);

                for (int j=0; j < argc; j++) {
                        printf("%d %s\n", j, argv[j]);
                }
        }
#endif

#if 1
        if (argc == 1) {
                usage();
                exit(EINVAL);
        }
#endif

        if (op == OP_PAXOSDUMP || op == OP_BASE64) {
                ret = env_init_simple("lich.inspect");
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                ng.offline = 1;
        } else if (op == OP_CHUNK_DUMP || op == OP_CRC32) {

        } else {
                ret = __inspect_init(op);
                if (ret && alone == 0) {
                        GOTO(err_ret, ret);
                }
        }

        if (path) {
                ret = path_split(path, pool, path2);
                if (unlikely(ret)) {
                        if (!strlen(pool) && (op == OP_LIST ||
                                op == OP_SCAN || op == OP_RECOVER)) {
                                /*will list all pools*/
                        } else
                                GOTO(err_ret, ret);
                }
        }

        switch (op) {
        case OP_LIST:
                if (strlen(pool)) {
                        ret = __lich_list(pool, path2);
                } else {
                        ret = utils_pool_list(1);
                }

                if (unlikely(ret))
                        GOTO(err_ret, ret);
                break;
        case OP_MKDIR:
                ec.plugin = (!k || !r) ? PLUGIN_NULL : PLGUIN_EC_ISA;
                ec.tech = (!k || !r) ? TECH_NULL : TECH_ISA_SSE;
                ec.k = k;
                ec.m = k + r;

                if (ec.plugin == PLGUIN_EC_ISA) {
                        if (ec.k <= 0 || ec.m <= 0 || ec.k > EC_KMAX || ec.m > EC_MMAX) {
                                ret = EPERM;
                                GOTO(err_ret, ret);
                        }
                }

                ret = utils_mkdir(pool, path2, parents, &ec, "");
                if (unlikely(ret))
                        GOTO(err_ret, ret);
                break;
        case OP_TOUCH:
                ret = utils_touch(pool, path2, priority);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_UNLINK:
                ret = utils_rmvol(pool, path2, 0);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_RMDIR:
                ret = utils_rmdir(pool, path2);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_CHUNK_INFO:
                ret = __lich_dump(pool, path2, verbose, pithy);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_CMP:
                ret = __lich_cmp(pool, path2);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
                break;
        case OP_RECOVER:
                recovery.deep = deep;
                recovery.verbose = verbose;
                recovery.thread = thread;

#if 0
                /* output too many logs */
                if (verbose) {
                        dbg_goto(1);
                } else {
                        dbg_goto(0);
                }
#endif

retry:
                if (strcmp(path, "/") == 0) {
                        strcpy(path2, path);
                        recovery.path = path2;
                        ret = system_pool_iterator(recovery_bypath, (void *)&recovery);
                } else {
                        recovery.path = path2;
                        ret = recovery_bypath(pool, (void *)&recovery);
                }

                if (unlikely(ret)) {
                        if (ret == EAGAIN) {
                                USLEEP_RETRY(err_ret, ret, retry, retry, gloconf.rpc_timeout * 4, (1000 * 1000));
                        } else
                                GOTO(err_ret, ret);
                }

                break;
        case OP_CHUNKSET:
                ret = __lich_chunkset(pool, path2, argv[argc - 2], argv[argc - 1]);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
                break;
        case OP_MOVE:
                ret = __lich_move(pool, path2, to, async);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_ALLOCATE:
                ret = __lich_allocate(pool, path2, !pithy, fill);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_TRUNCATE:
                ret = __lich_truncate(pool, path2, to);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_STAT:
                ret = __lich_stat(pool, path2, full, verbose);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_LOOKUP:
                if (strlen(pool)) {
                        ret = __lich_lookup(pool, path2);
                } else {
                        ret = system_pool_iterator1(__lich_lookup, (void *)path2);
                }

                if (unlikely(ret))
                        GOTO(err_ret, ret);

                break;
        case OP_SCAN:
                recovery.deep = deep;
                recovery.verbose = verbose;
                recovery.thread = thread;
                if (strcmp(path, "/") == 0) {
                        strcpy(path2, path);
                        recovery.path = path2;
                        ret = system_pool_iterator(recovery_scan, (void *)&recovery);
                } else {
                        recovery.path = path2;
                        ret = recovery_scan((void *)pool, (void *)&recovery);
                }
                if (unlikely(ret))
                        GOTO(err_ret, ret);
                break;
        case OP_MD5SUM:
                ret = __lich_md5sum(pool, path2);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
                break;
        case OP_LOCALIZE:
                _argc = argc;

                if (_argc == 4) {
                        int num;

                        if (!is_zero_one_char(argv[argc - 1])) {
                                ret = EINVAL;
                                GOTO(err_ret, ret);
                        }

                        num = atoi(argv[argc - 1]);
                        ret = lich_localize_set(pool, path2, num);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else if (_argc == 3) {
                        ret = lich_localize_get(pool, path2);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        ret = EINVAL;
                        GOTO(err_ret, ret);
                }

                break;
        case OP_MULTPATH:
                _argc = argc;

                if (_argc == 4) {
                        int num;

                        if (!is_zero_one_char(argv[argc - 1])) {
                                ret = EINVAL;
                                GOTO(err_ret, ret);
                        }

                        num = atoi(argv[argc - 1]);
                        ret = lich_multpath_set(pool, path2, num);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else if (_argc == 3) {
                        ret = lich_multpath_get(pool, path2);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        GOTO(err_ret, ret);
                }

                break;
        case OP_READRAW:
                if (argc == 4) {
                        int num;

                        if (!is_zero_one_char(argv[3])) {
                                ret = EINVAL;
                                GOTO(err_ret, ret);
                        }

                        num = atoi(argv[3]);
                        ret = lich_readraw_set(pool, path2, num);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else if (argc == 3) {
                        ret = lich_readraw_get(pool, path2);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        GOTO(err_ret, ret);
                }

                break;
        case OP_CHUNK_MOVE:
                ret = __lich_chunk_move(pool, path2, to);
                if (unlikely(ret))
                        goto err_ret;

                break;
        case OP_CHUNK_STAT:
                ret = __lich_chunk_stat(pool, path2);
                if (unlikely(ret))
                        goto err_ret;

                break;
        case OP_DB_DUMP:
                ret = __lich_db_dump(path2, name);
                if (unlikely(ret))
                        goto err_ret;

                break;
         case OP_DB_MODE:
                ret = __lich_db_mode_dump(path2);
                if (unlikely(ret))
                        goto err_ret;

                break;
        case OP_BASE64:
                _argc = argc;

                if (pithy) {
                        if (_argc != 5) {
                                ret = EINVAL;
                                GOTO(err_ret, ret);
                        }
                } else {
                        if (_argc != 4) {
                                ret = EINVAL;
                                GOTO(err_ret, ret);
                        }
                }

                if (!strncmp(name, "encode", strlen(name))) {
                        to = argv[argc - 1];

                        ret = path_split(to, pool, path2);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);

                        ret = __lich_base64_encode(pool, path2, pithy);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else if (!strncmp(name, "decode", strlen(name))) {
                        to = argv[argc - 1];

                        ret = __lich_base64_decode(pool, to, pithy);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        usage();
                        EXIT(EINVAL);
                }

                break;
        case OP_CRC32:
                ret = chdir(cwdpath);
                _argc = argc;

                if (_argc == 3) {
                        ret = __lich_crc32(name);
                        if (ret)
                                GOTO(err_ret, ret);
                } else {
                        usage();
                        EXIT(EINVAL);
                }

                break;
        case OP_CHUNK_DUMP:
                ret = chdir(cwdpath);
                _argc = argc;

                if (_argc == 3) {
                        ret = __lich_chunk_dump(pool, path2, NULL);
                        if (ret)
                                goto err_ret;
                } else if (_argc == 4) {
                        ret = __lich_chunk_dump(pool, path2, argv[argc - 1]);
                        if (ret)
                                goto err_ret;
                } else {
                        usage();
                        EXIT(EINVAL);
                }


                break;
        case OP_CHUNK_FILL:
                ret = chdir(cwdpath);
                _argc = argc;

                if (_argc == 3 || (_argc == 4 && fill)) {
                        ret = __lich_chunk_zerofill(pool, path2);
                        if (ret)
                                goto err_ret;
                } else if (_argc == 4 || (_argc == 5 && fill)) {
                        ret = __lich_chunk_fill(pool, path2, argv[argc - 1], fill);
                        if (ret)
                                goto err_ret;
                } else {
                        usage();
                        EXIT(EINVAL);
                }

                break;
        case OP_CHUNK_CLEANUP:
                ret = chdir(cwdpath);
                ret = __lich_chunk_cleanup(pool, path2);
                if (unlikely(ret))
                        goto err_ret;

                break;
        case OP_CONNECTION:
                ret = __lich_connection(pool, path2);
                if (unlikely(ret))
                        goto err_ret;

                break;
        case OP_BOARDCAST:
                ret = __lich_boardcast(path2);
                if (ret)
                        goto err_ret;

                break;
        case OP_RMVOL_FORCE:
                ret = __lich_rmvol_force(pool, path2);
                if (ret)
                        goto err_ret;

                break;
        case OP_DISPATCH:
                ret = __lich_dispatch(dispatch_pool, dispatch_site, repnum);
                if (ret)
                        goto err_ret;

                break;

        case OP_VFMSET:
                if (strlen(pool)) {
                        ret = __lich_vfm_set(pool, path2, to);
                } else {
                        ret = __lich_vfm_set(NULL, path, to);
                }
                if (ret)
                        goto err_ret;

                break;
        case OP_VFMGET:
                if (strlen(pool)) {
                        ret = __lich_vfm_get(pool, path2);
                } else {
                        ret = __lich_vfm_get(NULL, path);
                }
                if (ret)
                        goto err_ret;

                break;
        case OP_DISCONNECT:
                {
                        YASSERT(argc == 5);
                        YASSERT(strlen(pool) > 0);

                        ret = __lich_disconnect(pool, path2, argv[3], argv[4]);
                        if (unlikely(ret))
                                goto err_ret;
                }
                break;
        default:
                usage();
                EXIT(EINVAL);
        }

        //DBUG("test...........\n");

        return 0;
err_ret:
        fprintf(stderr, "error:%s\n", strerror(ret));
        exit(_errno(ret));
}
